﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using ID3TagLib;
using System.Xml.Serialization;
using System.Runtime.InteropServices;
using System.Xml;
using System.Data;
using Microsoft.WindowsAPICodePack.Shell;
using Ionic.Zip;
using System.Linq;

namespace Home.ArtDB
{
    /// <summary>
    /// 
    /// </summary>
    public class ArtEpubFile : ArtFile
    {

        /// <summary></summary>
        private ZipFile _zip;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="parentCollection"></param>
        /// <param name="filename"></param>
        public ArtEpubFile(ArtFileCollection parentCollection, string filename) : base(parentCollection, filename) { }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="parentCollection"></param>
        /// <param name="row"></param>
        public ArtEpubFile(ArtFileCollection parentCollection, DataRow row) : base(parentCollection, row) { }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        public override void Load(string filename)
        {
            base.Load(filename);

            _zip = new ZipFile(filename);
            Valid = _zip != null;

            if (Valid)
            {
                XmlDocument doc = ReadOpfFile(filename);

                if (Valid)
                {
                    List<XmlNode> metaNods = doc.GetElementsByTagName("meta").Cast<XmlNode>().ToList();
                    List<XmlNode> dcNods = doc.GetElementsByTagName("dc").Cast<XmlNode>().ToList();

                    Track = (int)Convert.ToDouble(GetMetaData(metaNods, "calibre:series_index", "0").Replace(".", ","));

                    Album = GetMetaData(metaNods, "calibre:series");
                    Title = GetMetaData(metaNods, "calibre:title_sort");
                    Title = Title.Equals(string.Empty) ? GetMetaData(doc, "dc:title") : Title;

                    Comment = GetMetaData(doc, "dc:description");

                    Composer = GetMetaData(doc, "dc:creator");
                    Artist = Composer;

                    Language = GetMetaData(doc, "dc:language");
                    //Language = Language.Equals("de", StringComparison.OrdinalIgnoreCase) ? "E-Buch" : (Language.Equals("ru", StringComparison.OrdinalIgnoreCase) ? "Э-Книга" : Language);
                    Genre = GetMetaData(doc, "dc:subject");

                    HomeID = GetMetaData(doc, "dc:identifier");
                    HomeID = HomeID.Equals(string.Empty) ? "ebook_" + Guid.NewGuid().ToString() : HomeID;

                    Size = 0;
                    Duration = 0;
                    //Year = _file.Date.Count.Equals(0) ? "" : _file.Date[0].Date;
                }
            }

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="metaNodes"></param>
        /// <param name="name"></param>
        /// <param name="defaultResult"></param>
        /// <returns></returns>
        private string GetMetaData(List<XmlNode> metaNodes, string name, string defaultResult = "")
        {
            XmlNode metaNode = metaNodes.Where(item => item.Attributes["name"] != null && item.Attributes["name"].Value.Equals(name)).FirstOrDefault();

            if (metaNode != default(XmlNode))
            {
                defaultResult = metaNode.Attributes["content"].Value;
            }

            return defaultResult;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="doc"></param>
        /// <param name="name"></param>
        /// <param name="defaultResult"></param>
        /// <returns></returns>
        private string GetMetaData(XmlDocument doc, string name, string defaultResult = "")
        {
            XmlNode metaNode = doc.GetElementsByTagName(name).Cast<XmlNode>().FirstOrDefault();

            if (metaNode != default(XmlNode))
            {
                defaultResult = metaNode.InnerText;
            }

            return defaultResult;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="epubFilename"></param>
        /// <returns></returns>
        private XmlDocument ReadOpfFile(string epubFilename)
        {
            XmlDocument opfDoc = new XmlDocument();

            //Meta-Inf auslesen
            //    <rootfile full-path="OEBPS/Content.opf" media-type="application/oebps-package+xml" />
            Dictionary<string, string> containerFileContents = Open(epubFilename, "META-INF\\container.xml");

            Valid = containerFileContents != null;

            if (Valid && containerFileContents.ContainsKey("container.xml"))
            {
                XmlDocument doc = new XmlDocument();
                try
                {
                    doc.LoadXml(containerFileContents["container.xml"]);
                }
                catch
                {
                    Valid = false;
                }

                if (Valid)
                {
                    XmlNode rootNode = doc.GetElementsByTagName("rootfile").Cast<XmlNode>().FirstOrDefault();

                    if (rootNode != null)
                    {
                        string opfRootPath = rootNode.Attributes["full-path"] == null ? string.Empty : rootNode.Attributes["full-path"].Value;

                        if (!opfRootPath.Equals(string.Empty))
                        {
                            //ORF-Datei auslesen
                            Dictionary<string, string> opfFileContents = Open(epubFilename, opfRootPath);

                            Valid = opfFileContents != null && opfFileContents.Count.Equals(1);

                            if (Valid)
                            {
                                try
                                {
                                    opfDoc.LoadXml(opfFileContents.ElementAt(0).Value);
                                }
                                catch
                                {
                                    Valid = false;
                                }
                            }
                        }
                    }
                }
            }
            return opfDoc;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="filename"></param>
        /// <param name="compressedFileNames"></param>
        /// <returns></returns>
        private Dictionary<string, string> Open(string filename, params string[] compressedFileNames)
        {
            string destinationPath = Path.Combine(SpecialFolder.GetPath(SpecialFolder.SpecialFolderNames.CSIDL_COMMON_APPDATA), "artdb_" + Guid.NewGuid().ToString());
            DirectoryInfo destinationDirInfo = Directory.CreateDirectory(destinationPath);

            Dictionary<string, string> fileContents = new Dictionary<string, string>();

            //zip.ExtractProgress += new EventHandler<ExtractProgressEventArgs>(Explizit_ExtractProgress);
            _zip.ZipError += new EventHandler<ZipErrorEventArgs>(zip_ZipError);

            if (compressedFileNames.Length.Equals(0))
            {
                try
                {
                    _zip.ExtractAll(destinationPath, ExtractExistingFileAction.OverwriteSilently);
                }
                catch (Exception ex)
                {
                    fileContents = null;
                }
            }
            else
            {
                int fileCount = compressedFileNames.Length;
                for (int i = 0; i < fileCount; i++)
                {
                    string fileName = compressedFileNames[i];

                    string tempPath = @"z:\\" + fileName;
                    string archivPath = Path.GetDirectoryName(tempPath).Replace("z:\\", "");

                    string compressedFileName = Path.GetFileName(tempPath);
                    string localPath = Path.Combine(Path.Combine(destinationPath, archivPath), compressedFileName);

                    try
                    {
                        _zip.ExtractSelectedEntries(compressedFileName, archivPath, destinationPath, ExtractExistingFileAction.OverwriteSilently);

                        int extractedFileCount = Directory.GetFiles(destinationPath, "*.*", SearchOption.AllDirectories).Length;
                    }
                    catch (Exception ex)
                    {
                        fileContents = null;
                    }
                }
            }

            if (fileContents != null)
            {
                foreach (string fileName in Directory.EnumerateFiles(destinationPath, "*.*", SearchOption.AllDirectories))
                {
                    fileContents.Add(Path.GetFileName(fileName), File.ReadAllText(fileName));
                }
            }

            destinationDirInfo.Delete(true);

            return fileContents;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void zip_ZipError(object sender, ZipErrorEventArgs e)
        {

        }

        /// <summary>
        /// 
        /// </summary>
        public override void Save()
        {
            base.Save();

            //_file.Save(FileName);
        }

    }

}
